'''
Created on Aug 16, 2009

@author: mkiyer
'''

from veggie.align.alignment import Alignment
import sys

cdef extern from "stdio.h" nogil:
    ctypedef struct FILE
    int sscanf(char * str, char * format, ...)
    int sprintf(char *str, char *format, ...)

cdef extern from "string.h":
    char *strcpy(char *dest, char *src)
    int strlen(char *s)
    char *strsep(char **stringp, char *delim)
    char *strdup(char *s)
    int strncmp(char *s1, char *s2, Py_ssize_t n)
    char *strrchr(char *s, int c)

cdef extern from "Python.h":
    FILE* PyFile_AsFile(object)
    object PyString_FromStringAndSize(char *v, int len)
    object PyString_FromString(char *v)
    int PyString_AsStringAndSize(object obj, char **buffer, Py_ssize_t* length) except -1

cdef class BowtieParser(object):
    '''
    The    bowtie aligner outputs each alignment on a separate line. Each line 
    is a collection of 8 fields separated by tabs; from left to right, 
    the fields are:

    1. Name of read that aligned
    2. Orientation of read in the alignment, - for reverse complement, 
        + otherwise
    3. Name of reference sequence where alignment occurs, or ordinal ID if 
        no name was provided
    4. 0-based offset into the forward reference strand where leftmost 
        character of the alignment occurs
    5. Read sequence (reverse-complemented if orientation is -)
    6. ASCII-encoded read qualities (reversed if orientation is -). 
       The encoded quality values are on the Phred scale and the 
       encoding is ASCII-offset by 33 (ASCII char !).
    7. Number of other instances where the same read aligns against the same
       reference characters as were aligned against in this alignment. This 
       is not the number of other places the read aligns with the same 
       number of mismatches. The number in this column is generally not a 
       good proxy for that number (e.g., the number in this column may be 
       '0' while the number of other alignments with the same number of 
       mismatches might be large). This column was previously described as 
       "Reserved".
    8. Comma-separated list of mismatch descriptors. If there are no 
       mismatches in the alignment, this field is empty. A single descriptor 
       has the format offset:reference-base>read-base. The offset is 
       expressed as a 0-based offset from the high-quality (5') end of 
       the read.
    '''
    cdef file fhd
    cdef FILE * fp
    
    def __cinit__(BowtieParser self, file fhd):
        self.fhd = fhd
        self.fp = PyFile_AsFile(self.fhd)

    def __iter__(self):
        return self
        
    def __next__(self):
        thisline = self.fhd.next()
        # split the line into fields and extract relevant information
        fields = thisline.strip().split('\t')
        chrom, chrom_start, chrom_end, strand = parse_fields(fields)
        # uncomment to test the parser against the python version
#        chrom2, chrom_start2, chrom_end2, strand2 = python_parse_fields(fields)
#        assert chrom == chrom2
#        assert chrom_start == chrom_start2
#        assert chrom_end == chrom_end2
#        assert strand == strand2
#        print thisline, chrom, chrom_start, chrom_end, strand
        return chrom, chrom_start, chrom_end, strand
    
def python_parse_fields(fields):
    name = fields[0]
    strand = fields[1]
    # convert the chromosome mapping field to ucsc chromosome format
    chrom = fields[2].split('|')[-1]
    if chrom.startswith('NC'):
        chrom = str(int(chrom.split('_')[-1]))                    
        if chrom == '23':
            chrom = 'X'
        elif chrom == '24':
            chrom = 'Y'
        chrom = 'chr' + str(chrom)
    chrom_start = int(fields[3])
    chrom_end = chrom_start + len(fields[4])
    # TODO: handle mismatches        
    return chrom, chrom_start, chrom_end, strand

cdef parse_fields(object fields):
    # parse strand
    cdef char strand[2]
    sscanf(fields[1], "%s", strand)

    # convert the chromosome field from a python string to a C-style char*
    cdef char *chrombuf
    cdef Py_ssize_t len
    PyString_AsStringAndSize(fields[2], &chrombuf, &len)

    # convert the chromosome field to ucsc chromosome format        
    cdef char chrom[100]
    cdef int chromnum
    cdef char *pChrom

    pChrom = strrchr(chrombuf, '|')
    if pChrom == NULL:
        # this is an unconventional chrom name like 'chrM'
        # just copy it directly
        strcpy(chrom, chrombuf)
    else:
        # advanced past the '|' to the chromosome name
        pChrom += 1
        # see if chromosome name is of the refseq type NC_0000XX
        if strncmp(pChrom, "NC_", 3) != 0:
            # not a refseq chrom name, just copy the chromosome info
            strcpy(chrom, pChrom)
        else:
            # advanced past the 'NC_' and grab the chromosome number
            pChrom += 3
            sscanf(pChrom, "%d", &chromnum)
            if chromnum == 23:
                strcpy(chrom, "chrX")
            elif chromnum == 24:
                strcpy(chrom, "chrY")
            else:
                sprintf(chrom, "chr%d", chromnum)
    
    # chrom start and end
    cdef int chrom_start
    cdef int chrom_end
    sscanf(fields[3], "%d", &chrom_start)
    chrom_end = chrom_start + strlen(fields[4])
    return chrom, chrom_start, chrom_end, strand

#def test_next(thisline):
#    fields = thisline.strip().split()
#    print fields        
#    print parse_fields(fields)
    #print chrom, chrom_start, chrom_end, strand
##copy a string by converting it to C values and back
#cdef copyString(object input_string):
#    cdef char *buffer
#    cdef Py_ssize_t len
#    PyString_AsStringAndSize(input_string, &buffer, &len)
#    return PyString_FromStringAndSize(buffer, len) #return a new string object
